#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

const int N=110;
bool res;
int done[N];
int n,m;
int gcd[N][N],lcm[N][N],a[N],amax[N];
int cnt;

int getGcd(int a,int b)
{
	if(!a) return b;
	else return getGcd(b%a,a);
}
bool judge(int a,int b,int g,int l)
{
	//printf("judge: %d %d %d %d\n",a,b,g,l);
	if(a>b) swap(a,b);
	int t=getGcd(a,b);
	if(t!=g||a/t*b!=l) return false;
	else return true;
}
bool dfs(int pos)
{
	int t;
	done[pos]=cnt;
	//printf("dfs: a[%d]=%d \n",pos,a[pos]);
	for(int to=1;to<=n;to++) if(gcd[pos][to]!=-1)
	{
		if(done[to]!=cnt)
		{
			t=lcm[pos][to]/a[pos]*gcd[pos][to];
		//	printf("%d %d gcd %d lcm %d\n",pos,to,gcd[pos][to],lcm[pos][to]);
			if(!judge(a[pos],t,gcd[pos][to],lcm[pos][to])) return false;
			else
			{
				a[to]=t;
				if(!dfs(to)) return false;
			}
		}
		else
		{
			if(!judge(a[pos],a[to],gcd[pos][to],lcm[pos][to])) return false;
		}
	}
	return true;
}
int main()
{
	while(cin>>n>>m)
	{
		fill(gcd[0],gcd[0]+N*N,-1);
		fill(lcm[0],lcm[0]+N*N,-1);
		fill(amax,amax+N,1<<29);
		fill(done,done+N,-1);
		for(int i=0,v1,v2,ag,al;i<m;i++)
		{
			cin>>v1>>v2>>ag>>al;
			gcd[v2][v1]=gcd[v1][v2]=ag;
			lcm[v2][v1]=lcm[v1][v2]=al;
			amax[v2]=min(amax[v2],al);
			amax[v1]=min(amax[v1],al);
		}
		cnt=0;
		for(int i=1;i<=n;i++) if(done[i]==-1)
		{
			res=false;
			for(int j=1;j<=amax[i];j++) if(amax[i]%j==0)
			{
				//printf("a[%d]=%d \n ",i,amax[i]);
				a[i]=j;
				cnt++;
				if(dfs(i))
				{
					res=true;
					break;
				}
			}
			if(!res) break;
		}
		if(!res) cout<<"NO"<<endl;
		else
		{
			cout<<"YES"<<endl;
			for(int i=1;i<=n;i++) cout<<a[i]<<" ";
			cout<<endl;
		}
	}
	return 0;
}
/*
3 2
3 2 1 2
3 1 1 10
2 1
1 2 3 7
*/
